package standalone;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;

import corpus.Word;
import corpus.WordNet;

/**
 * Monte Carlo algorithm for polarity database. Creates a polarity database from
 * a smalls set of seed words by walking randomly in a graph of words, ie
 * WordNet.
 * 
 * TODO
 * Optimizations 
 *   Solve the problem with conflicts in hashmaps using (use hash instead of tree) 
 *     a) better hashCode and 
 *     b) lists in hashmap
 *   Alt. use radix-tree
 * 
 * Perhaps use the length difference as weight for strength.
 * 
 * Adjust probability function
 *     a) better random function?
 *     b) weight of synonyms vs hypernyms vs related
 * 
 * Calculate average over 10 times.
 */
public class GeneratePolarityDatabase {

	public static void main(String[] args) throws IOException {
		new GeneratePolarityDatabase("C:\\Program Files (x86)\\WordNet\\2.1",
				new File("corpus/polaritydatabase/seed-neg.txt"), new File(
						"corpus/polaritydatabase/seed-pos.txt"));
	}

	/**
	 * These constants are the parameters for the algorithm
	 */
	public static final int I = 100; // 100 1000
	public static final int M = 15; // 250 30

	private WordNet dict;
	private Map<Word, Set<Word>> links;

	private Map<Word, Integer> posPolarity;
	private Map<Word, Integer> negPolarity;

//	private Map<Word, Integer> posTimesHit;
//	private Map<Word, Integer> negTimesHit;

	private Random r = new Random();

	public GeneratePolarityDatabase(String wordNetHome, File seedNeg,
			File seedPos) throws IOException {
		System.out.println("Loading WordNet");
		File output = new File("corpus/polaritydatabase/pol-database.txt");
		FileOutputStream fos = new FileOutputStream(output);
		PrintStream ps = new PrintStream(fos);
		dict = new WordNet(wordNetHome);

		links = new TreeMap<Word, Set<Word>>();
		Set<Word> words = dict.words();
		for (Word w : words) {
			Set<Word> rel = dict.relatedWords(w);
			links.put(w, rel);
		}

//		negTimesHit = new TreeMap<Word, Integer>();
//		posTimesHit = new TreeMap<Word, Integer>();
		negPolarity = new TreeMap<Word, Integer>();
		posPolarity = new TreeMap<Word, Integer>();
		loadSeed(seedNeg, false);
		loadSeed(seedPos, true);

		System.out.println("Starting walk");
		walk();

		System.out.println("pos: " + posPolarity.size());
		System.out.println("neg: " + negPolarity.size());

		for (Entry<Word, Integer> e : negPolarity.entrySet()) {
			if (posPolarity.containsKey(e.getKey())) {
				posPolarity.put(e.getKey(),
						posPolarity.get(e.getKey()) - e.getValue());
			} else {
				posPolarity.put(e.getKey(), -e.getValue());
			}
		}

		Map<Word, Integer> sorted = sort(posPolarity);

		ps.println("Database size " + sorted.size());
		for (Entry<Word, Integer> e : sorted.entrySet()) {
			ps.println(e.getValue() + " " + e.getKey());
		}
	}

	private Map<Word, Integer> sort(Map<Word, Integer> unsortMap) {
		List list = new LinkedList(unsortMap.entrySet());

		Comparator comparator = new Comparator() {

			public int compare(final Object o1, final Object o2) {
				return ((Comparable) ((Map.Entry) (o2)).getValue())
						.compareTo(((Map.Entry) (o1)).getValue());
			}
		};
		Collections.sort(list, comparator);

		// put sorted list into map again
		Map sortedMap = new LinkedHashMap();
		for (Iterator it = list.iterator(); it.hasNext();) {
			Map.Entry entry = (Map.Entry) it.next();
			sortedMap.put(entry.getKey(), entry.getValue());
		}
		return sortedMap;
	}

	private void increment(Map<Word, Integer> m, Word w, int plus) {
		int i = m.get(w);
		m.put(w, i + plus);
	}

	private void walk() {
		int cntr = 0;
		for (Word w : links.keySet()) {
			Word current = w;
			// repeat I times
			for (int i = 0; i < I; i++) {
				// walk M steps
				for (int m = 0; m < M; m++) {
					Set<Word> synonyms = links.get(current);
					if (synonyms == null) {
						System.out.println(current + " has no synonyms");
						break;
					}

					current = randSelect(synonyms);
					if (posPolarity.containsKey(current)) {
						increment(posPolarity, current, M-m);
//						average(posPolarity, current, M - m,
//								posTimesHit.get(current));
//						increment(posTimesHit, current, 1);
						break;
					} else if (negPolarity.containsKey(current)) {
						increment(negPolarity, current, M-m);
//						average(negPolarity, current, M - m,
//								negTimesHit.get(current));
//						increment(negTimesHit, current, 1);
						break;
					}
				}
			}
			if ((++cntr) % 500 == 0)
				System.out.println(cntr + " words walked");
		}
	}

	private Word randSelect(Set<Word> words) {
		Object[] array = words.toArray();
		return (Word) array[r.nextInt(array.length)];
	}

	private void loadSeed(File file, boolean positiveWords) throws IOException {
		Scanner scr = new Scanner(file);
		while (scr.hasNextLine()) {
//			Scanner lineScr = new Scanner(scr.nextLine());
//			String lemma = lineScr.next();
//			String pos = lineScr.next();

			// TODO redo
//			Set<Word> words = links.get(new Word(lemma, pos));
//			for (Word word : words) {
//				if (positiveWords) {
//					posPolarity.put(word, 1);
//					posTimesHit.put(word, 1);
//				} else {
//					negPolarity.put(word, 1);
//					negTimesHit.put(word, 1);
//				}
//			}
		}
	}
}
